#ifndef LIME_LIMESDR_X3_H
#define LIME_LIMESDR_X3_H

#include "chips/CDCM6208/CDCM6208.h"
#include "boards/LMS7002M_SDRDevice.h"

#include <vector>
#include <array>
#include <memory>

namespace lime {

class LimePCIe;
class CrestFactorReduction;
class ISerialPort;
class ISPI;

/** @brief Class for managing the LimeSDR X3 device. */
class LimeSDR_X3 : public LMS7002M_SDRDevice
{
  public:
    LimeSDR_X3() = delete;
    LimeSDR_X3(std::shared_ptr<ISerialPort> control, std::vector<std::shared_ptr<LimePCIe>> trxStreams);
    ~LimeSDR_X3();

    OpStatus Configure(const SDRConfig& config, uint8_t socIndex) override;

    OpStatus Init() override;
    OpStatus Reset() override;

    double GetSampleRate(uint8_t moduleIndex, TRXDir trx, uint8_t channel, uint32_t* rf_samplerate = nullptr) override;
    OpStatus SetSampleRate(uint8_t moduleIndex, TRXDir trx, uint8_t channel, double sampleRate, uint8_t oversample) override;

    double GetClockFreq(uint8_t clk_id, uint8_t channel) override;
    OpStatus SetClockFreq(uint8_t clk_id, double freq, uint8_t channel) override;

    OpStatus SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override;

    OpStatus CustomParameterWrite(const std::vector<CustomParameterIO>& parameters) override;
    OpStatus CustomParameterRead(std::vector<CustomParameterIO>& parameters) override;

    OpStatus UploadMemory(
        eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback) override;
    OpStatus MemoryWrite(std::shared_ptr<DataStorage> storage, Region region, const void* data) override;
    OpStatus MemoryRead(std::shared_ptr<DataStorage> storage, Region region, void* data) override;
    OpStatus UploadTxWaveform(const StreamConfig& config, uint8_t moduleIndex, const void** samples, uint32_t count) override;

    std::unique_ptr<lime::RFStream> StreamCreate(const StreamConfig& config, uint8_t moduleIndex) override;

  private:
    OpStatus InitLMS1(bool skipTune = false);
    OpStatus InitLMS2(bool skipTune = false);
    OpStatus InitLMS3(bool skipTune = false);
    OpStatus ConfigureLMS1(const SDRConfig& config);
    OpStatus ConfigureLMS2(const SDRConfig& config);
    OpStatus ConfigureLMS3(const SDRConfig& config);
    void LMS1_PA_Enable(uint8_t chan, bool enabled);
    void LMS1SetPath(TRXDir dir, uint8_t chan, uint8_t pathId);
    void LMS2SetPath(TRXDir dir, uint8_t chan, uint8_t path);
    void LMS2_PA_LNA_Enable(uint8_t chan, bool PAenabled, bool LNAenabled);
    void LMS3SetPath(TRXDir dir, uint8_t chan, uint8_t path);
    void LMS3_SetSampleRate_ExternalDAC(double chA_Hz, double chB_Hz);
    static OpStatus LMS1_UpdateFPGAInterface(void* userData);

    void LMS2_SetSampleRate(double f_Hz, uint8_t oversample);

    enum class ePathLMS1_Rx : uint8_t { NONE, LNAH, LNAL, LNAW };
    enum class ePathLMS1_Tx : uint8_t { NONE, BAND1, BAND2 };
    enum class ePathLMS2_Rx : uint8_t { NONE, TDD, FDD, CALIBRATION };
    enum class ePathLMS2_Tx : uint8_t { NONE, TDD, FDD };
    enum class ePathLMS3_Rx : uint8_t { NONE, LNAH, LNAL, LNAW };

    void SetLMSPath(const TRXDir dir, const ChannelConfig::Direction& trx, const int ch, const uint8_t socIndex);

    std::shared_ptr<ISerialPort> controlPort;
    std::unique_ptr<CDCM_Dev> mClockGeneratorCDCM;
    std::vector<std::shared_ptr<LimePCIe>> mTRXStreamPorts;
    std::unique_ptr<CrestFactorReduction> mEqualizer;

    std::array<std::shared_ptr<ISPI>, 3> mLMS7002Mcomms;
    std::shared_ptr<ISPI> mfpgaPort;
    bool mConfigInProgress;
};

} // namespace lime

#endif // LIME_LIMESDR_X3_H
